import { isEmpty as lodashIsEmpty, isNaN } from "lodash-es";

export interface EmptyOptions {
  /** 对字符串是否执行 `trim()` 再判断，默认 `true` */
  trimString?: boolean;
  /** 把 `false` 也视为空，默认 `false` */
  treatFalseAsEmpty?: boolean;
  /** 过滤对象属性时，哪些值也当做“无效”，默认只过滤 `null|undefined` */
  invalidObjectValues?: unknown[];
  /** 额外的自定义空值判断，返回 `true` 则视为空 */
  extraEmptyCheck?: (value: unknown) => boolean;
}

/**
 * 判断给定数据是否为空值。
 *
 * 空值的定义包括：
 * - `null` 或 `undefined`
 * - `NaN`
 * - 空字符串（可配置是否执行 `trim()`）
 * - 字符串形式的空对象/空数组：`'{}'`、`'[]'`
 * - 空数组 `[]`
 * - 空对象 `{}`（可配置是否忽略值为 null/undefined 的属性）
 * - 空的 Map、Set、TypedArray、Buffer
 *
 * 默认以下值**不视为**空值：
 * - 数字 `0`
 * - 布尔值 `false`
 * - 非空字符串、数组、对象、Map、Set 等
 *
 * @param value - 需要判断的任意数据。
 * @param options - 可选参数，用于控制空值的判定逻辑：
 * - `trimString`：是否在判断字符串为空前进行 `trim()`（默认 `true`）
 * - `treatFalseAsEmpty`：是否将布尔值 `false` 判定为空（默认 `false`）
 * - `invalidObjectValues`：对象中哪些属性值应被视为无效（默认 `[null, undefined]`）
 * - `extraEmptyCheck`：额外的自定义空值判断函数，返回 `true` 即认为是空值
 *
 * @returns 如果值被判定为空，则返回 `true`；否则返回 `false`。
 *
 * @example
 * ```ts
 * isEmptyData(null);                // true
 * isEmptyData('');                  // true
 * isEmptyData('   ');               // true（默认会 trim）
 * isEmptyData('{}');                // true
 * isEmptyData('[]');                // true
 * isEmptyData(0);                   // false
 * isEmptyData(false);               // false
 * isEmptyData([], {});              // true
 * isEmptyData({ a: null });         // true（默认忽略无效值）
 * isEmptyData('false', { trimString: false }); // false
 * ```
 */
export function isEmptyData(value: unknown, options: EmptyOptions = {}): boolean {
  const {
    trimString = true,
    treatFalseAsEmpty = false,
    invalidObjectValues = [null, undefined],
    extraEmptyCheck
  } = options;

  // 1. null / undefined
  if (value === null || value === undefined) return true;

  // 2. 自定义钩子优先
  if (extraEmptyCheck?.(value)) return true;

  // 3. 数字/布尔
  if (typeof value === 'number') {
    return isNaN(value);
  }
  if (typeof value === 'boolean') {
    return treatFalseAsEmpty && !value;
  }

  // 4. 字符串
  if (typeof value === 'string') {
    const str = trimString ? value.trim() : value;

    // 空字符串、"{}"、"[]"
    return str === '' || str === '{}' || str === '[]';
  }

  // 5. Map / Set / Array-like / Buffer / TypedArray / arguments
  //    直接委托给 lodash 的 isEmpty，会统一判断 .size/.length
  //    （注意 lodash 会把数字、布尔也当空，这里我们只在对象/容器阶段调用）
  // Buffer、TypedArray、arguments 也包含在 lodash 的 isEmpty 范畴内
  // 所以我们统统交给 lodash 来做最后判断
  if (typeof value === 'object' && (value instanceof Map || value instanceof Set || Array.isArray(value) || true)) {
    // 但为了不把普通对象误判到这一步，我们先排除“纯对象”
    if (!(value.constructor === Object)) {
      return lodashIsEmpty(value);
    }
  }

  // 6. 纯对象：先过滤掉值为 invalidObjectValues 的属性，再看剩余 key 数
  if (typeof value === 'object') {
    const obj = value as Record<string, unknown>;
    const keys = Object.keys(obj).filter(k => !invalidObjectValues.includes(obj[k]));
    return keys.length === 0;
  }

  // 7. 其它（函数、Symbol、Class 实例等）都视为非空
  return false;
}

export default isEmptyData;
